home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Database / RecordManager / Controller.m < prev    next >
Text File  |  1993-05-04  |  7KB  |  295 lines

  1. /* Controller.m:
  2.  * You may freely copy, distribute, and reuse the code in this example.
  3.  * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  4.  * fitness for any particular use.
  5.  *
  6.  *
  7.  */
  8.  
  9. #import <pwd.h>
  10. #import <sys/param.h>
  11. #import <indexing/IXRecordManager.h>
  12. #import <indexing/IXPostingList.h>
  13. #import <indexing/protocols.h>
  14. #import <btree/protocols.h>
  15. #import <btree/IXPostingSet.h>
  16. #import <store/IXStoreFile.h>
  17. #import <architecture/byte_order.h>
  18.  
  19. #import "Controller.h"
  20. #import "DataRecord.h"
  21.  
  22. #define STRINGLEN(A) (strlen((A)) + 1)
  23.  
  24. @implementation Controller
  25.  
  26. - free
  27. {
  28.     [recMan free];
  29.     if (postingList != nil)
  30.     postingList = [[postingList freeObjects] free];
  31.  
  32.     return [super free];
  33. }
  34.  
  35. /*
  36.  * Commits any pending transactions to disk.
  37.  */
  38. - commit
  39. {
  40.     [[recMan store] commitTransaction];
  41.     return self;
  42. }
  43.  
  44. /* TARGET ACTION METHODS */
  45.  
  46. - clear:sender
  47. {
  48.     [idField setStringValue:""];
  49.     [dataForm setStringValue:"" at:0];
  50.     [dataForm setStringValue:"" at:1];
  51.     return self;
  52. }
  53.  
  54. /*
  55.  * Adds a new record to the database.  A DataRecord is created and initialized
  56.  * from the fields in the dataForm.
  57.  */
  58. - addARecord:sender
  59. {
  60.     id newRec;
  61.     const char *stringInput;
  62.     const char *numberInput;
  63.     
  64.     /* create and setup the new record */
  65.     /* We don't allow empty input, since those records can't be retrieved
  66.      * later on
  67.      */
  68.     stringInput = (const char *)[dataForm stringValueAt:0];
  69.     numberInput = (const char *)[dataForm stringValueAt:1];
  70.     if ( (strcmp(stringInput, "") == 0) || (strcmp(numberInput, "") == 0) ){
  71.         NXRunAlertPanel("Error", "You must supply a string.", NULL, NULL, NULL);
  72.         return self;
  73.     }
  74.     newRec = [[DataRecord alloc] init];
  75.     [newRec setAString:[dataForm stringValueAt:0]];
  76.     [newRec setAnInt:[dataForm intValueAt:1]];
  77.     /* add it in */
  78.     [idField setIntValue:[recMan addRecord:newRec]];
  79.     [newRec free];
  80.     [self commit];
  81.     [dataForm selectTextAt:0];
  82.     return self;
  83. }
  84.  
  85. /*
  86.  * Delete the current record from the database.
  87.  */
  88. - deleteRecord:sender
  89. {
  90.     id retval;
  91.     unsigned int aHandle;
  92.     
  93.     aHandle = (unsigned)[idField intValue];
  94.     retval = [recMan removeRecord:aHandle];
  95.     if(retval != nil)
  96.     {
  97.     [self clear:self];
  98.     [self commit];
  99.     }
  100.  
  101.     return self;
  102. }
  103.  
  104. /*
  105.  * Changes the data fields of the current record.  This is done by creating a
  106.  * new record and replacing the existing one with the new one.
  107.  */
  108. - editRecord:sender
  109. {
  110.     id newRec;
  111.     
  112.     /* create and setup the replacement record */
  113.     newRec = [[DataRecord alloc] init];
  114.     [newRec setAString:[dataForm stringValueAt:0]];
  115.     [newRec setAnInt:[dataForm intValueAt:1]];
  116.     
  117.     /* replace it */
  118.     [recMan replaceRecord:[idField intValue] with:newRec];
  119.     [newRec free];
  120.     [self commit];
  121.     return self;
  122. }
  123.  
  124.  
  125. /*
  126.  * Searches for records in the database.
  127.  * If the allSwitch is set, we simply pull all records from the record manager.
  128.  * If the allSwitch is not set, we search on each attribute 'anding' the 
  129.  * results as we go along.
  130.  */
  131. - search:sender
  132. {
  133.     id cur;
  134.     char *searchString;
  135.     IXPostingSet *postingSet;
  136.     unsigned int intfld;
  137.  
  138.     if (postingList != nil)
  139.     postingList = [[postingList freeObjects] free];
  140.  
  141.     if(![allSwitch state])
  142.     {
  143.     postingSet = [[IXPostingSet alloc] init];
  144.  
  145.     /* first search the string attribute */
  146.     searchString = (char *)[searchForm stringValueAt:0];
  147.     if(searchString && *searchString != '\0')
  148.     {
  149.         cur = [recMan cursorForAttributeNamed:DRASTRINGNAME];
  150.         if([cur setKey:searchString andLength:STRINGLEN(searchString)])
  151.             [postingSet formUnionWithPostingsIn:cur];
  152.  
  153.         [cur free]; // must free cursor to avoid leaking.
  154.     }
  155.  
  156.     /* now search the integer attribute */
  157.     searchString = (char *)[searchForm stringValueAt:1];
  158.     if(searchString && *searchString != '\0')
  159.     {
  160.         cur = [recMan cursorForAttributeNamed:DRANINTNAME];
  161.         intfld = atoi(searchString);
  162.  
  163.         /* accumulate results in the IXPostingSet.
  164.            Do a logical AND if there were string matches above,
  165.            otherwise, just do a logical OR.
  166.          */
  167.         
  168.  
  169. /* Swap if this is a little-endian machine. */
  170.         if ( NXHostByteOrder() == NX_LittleEndian )
  171.             intfld = NXSwapInt(intfld);
  172.  
  173.  
  174.  
  175.         if([cur setKey:(void *)&intfld andLength:sizeof(unsigned int)])
  176.         {
  177.         if([postingSet count] > 0)
  178.             [postingSet formIntersectionWithPostingsIn:cur];
  179.         else
  180.             [postingSet formUnionWithPostingsIn:cur];
  181.         }
  182.     
  183.         [cur free]; // must free cursor to avoid leaking.
  184.     }
  185.  
  186.     [searchForm selectTextAt:0];
  187.     postingList = [[IXPostingList alloc] initWithSource:recMan andPostingsIn:postingSet];
  188.     [postingSet free];
  189.     } else
  190.     postingList = [recMan recordsForClass:[DataRecord class]];
  191.    
  192.     [recBrowser loadColumnZero];
  193.     return self;
  194. }
  195.  
  196. - browserHit:sender
  197. /*
  198.  * Responds to the user selecting a handle in the browser by retrieving
  199.  * the corresponding record and displaying its values in the dataForm.
  200.  */
  201. {
  202.     int col = [recBrowser selectedColumn];
  203.     id dr, matrix = [recBrowser matrixInColumn:col];
  204.     unsigned int handle;
  205.     
  206.     handle = [[matrix selectedCell] intValue];
  207.     [idField setIntValue:handle];
  208.     if((dr = [recMan readRecord:handle fromZone:[self zone]]) == nil)
  209.     {
  210.     NXRunAlertPanel("Error", "Cannot read record", NULL, NULL, NULL);
  211.     [self clear:self];
  212.     } else
  213.     {
  214.     [dataForm setStringValue:[dr aString] at:0];
  215.     [dataForm setIntValue:[dr anInt] at:1];
  216.     [dr free];
  217.     }
  218.  
  219.     return self;
  220. }
  221.  
  222. /* NXBROWSER DELEGATE METHODS */
  223.  
  224. - (int)browser:sender fillMatrix:matrix inColumn:(int)col
  225. {
  226.     id cell;
  227.     int i, rows = 0;
  228.  
  229.     if (postingList != nil)
  230.     rows = [postingList count];
  231.  
  232.     for(i = 0; i < rows; i++)
  233.     {
  234.     [matrix insertRowAt:i];
  235.     cell = [matrix cellAt:i :0];
  236.     [cell setIntValue:[postingList handleOfObjectAt:i]];
  237.     [cell setLoaded:YES];
  238.     [cell setLeaf:YES];
  239.     }
  240.  
  241.     return rows;
  242. }
  243.  
  244. /* APPLICATION DELEGATE METHODS */
  245.  
  246. - appDidInit:sender
  247. {
  248.     struct passwd *pw;
  249.     char path[MAXPATHLEN];
  250.  
  251.     pw = getpwuid(getuid());
  252.     sprintf(path, "%s/%s", pw->pw_dir, DBFILENAME);
  253.     fprintf(stderr, "path %s\n", path);
  254.     [recBrowser acceptArrowKeys:YES andSendActionMessages:YES];
  255.     if(access(path, F_OK) == -1)
  256.     {
  257.     /* create new database */
  258.     recMan = [[IXRecordManager alloc] initWithName:DBNAME inFile:path];
  259.     if (recMan == nil)
  260.     {
  261.         NXRunAlertPanel("Error", "Cannot create database.", NULL, NULL, NULL);
  262.         [NXApp terminate:self];
  263.     }
  264.  
  265.     [recMan addAttributeNamed:DRASTRINGNAME forSelector:@selector(aString)];
  266.     [recMan setComparator:&IXCompareMonocaseStrings andContext:NULL 
  267.         forAttributeNamed:DRASTRINGNAME];
  268.     
  269.     [recMan addAttributeNamed:DRANINTNAME forSelector:@selector(anInt)];
  270.     [recMan setComparator:&IXCompareLongs andContext:NULL 
  271.         forAttributeNamed:DRANINTNAME];
  272.     } else
  273.     {
  274.     /* open existing database */
  275.     recMan = [[IXRecordManager alloc] initFromName:DBNAME inFile:path forWriting:YES];
  276.     if (recMan == nil)
  277.     {
  278.         NXRunAlertPanel("Error", "Cannot open database.", NULL, NULL, NULL);
  279.         [NXApp terminate:self];
  280.     }
  281.     }
  282.  
  283.     [[recMan store] startTransaction];
  284.     [[recMan store] commitTransaction]; // don't want to leave a transaction pending
  285.     return self;
  286. }
  287.  
  288. -appWillTerminate:sender
  289. {
  290.     [self commit];
  291.     return self;
  292. }
  293.  
  294. @end
  295.